#ifndef COLOR_H
#define COLOR_H
/*
color.h
19/05/2014
psycommando@gmail.com

Description:
This header contains tools to represent colors in various formats.
Supports only RGBA components at this time !

#TODO: Is this even used anymore ? Clean this mess up !

No crappyrights. All wrongs reversed !
*/
#include <utility>
#include <vector>

namespace rgbcolorutils
{
	enum e_COLOR_COMPONENTS
	{
		e_CMP_RED,
		e_CMP_GREEN,
		e_CMP_BLUE,
		e_CMP_UNUSED // for bits that are reserved, but have no uses
	};

	//===============================================================================
    //                            BitMask Utility
    //===============================================================================
    //Thanks to James Kanze for this one :)
    template <unsigned int N> struct Mask
    {
        static unsigned int const value = (1 << N) | Mask<(N-1)>::value; //Recursively create a bitmask at compile time !
    };

    template <> struct Mask<0>
    {
        static unsigned int const value = 0; //when we get to 0, we want a 0 to stop the recursivity
    };

    //Shifts a value to the left at compile time !
    template <unsigned int MASK, unsigned int N> struct ShifLefttMask
    {
        static unsigned int const value = ( MASK << N );
    };

    unsigned int CreateABitMask( unsigned int length );
    unsigned int ShiftLeftABitMask( unsigned int shiftamount, unsigned int mask );

	//===============================================================================
    //                              COLOR FORMATS
    //===============================================================================
    struct RGB888
	{
		typedef unsigned int data_t;
		static const unsigned short NB_BYTES	  = 3;
		static const unsigned short NB_COMPONENTS = 3;
		static const std::pair<e_COLOR_COMPONENTS,unsigned short> COMPONENTS_LENGTH[NB_COMPONENTS]; //Contains both the order and nb of bits affected to each
	};

    struct XBGR8888
	{
		typedef unsigned int data_t;
		static const unsigned short NB_BYTES	  = 4;
		static const unsigned short NB_COMPONENTS = 4;
		static const std::pair<e_COLOR_COMPONENTS,unsigned short> COMPONENTS_LENGTH[NB_COMPONENTS]; //Contains both the order and nb of bits affected to each
	};

	struct BGR555
	{
		typedef unsigned short data_t;
		static const unsigned short NB_BYTES	  = 2;
		static const unsigned short NB_COMPONENTS = 4;
		static const std::pair<e_COLOR_COMPONENTS,unsigned short> COMPONENTS_LENGTH[NB_COMPONENTS]; //Contains both the order and nb of bits affected to each
	};


	//===============================================================================
    //                              color format helper
    //===============================================================================
	template <class FORMAT> class CColor
	{
	public:
        typedef FORMAT                  formatinfo_t;
        typedef typename FORMAT::data_t data_t;

        CColor()
        {
            std::fill_n( m_colordata, formatinfo_t::NB_COMPONENTS, 0 );
        }

        CColor( data_t color[] )
        {
            std::copy_n( color, getNBComponents(), m_colordata );
        }

        CColor( data_t r, data_t g, data_t b )
        {
            std::fill_n( m_colordata, formatinfo_t::NB_COMPONENTS, 0 );
            m_colordata[FindComponent(e_CMP_RED)]   = r;
            m_colordata[FindComponent(e_CMP_GREEN)] = g;
            m_colordata[FindComponent(e_CMP_BLUE)]  = b;
        }

        //static unsigned short getNBBytes()
        //{
        //    return formatinfo_t::NB_BYTES;
        //}

        static const std::pair<e_COLOR_COMPONENTS,unsigned short> & getComponentListAndBitSize()
        {
            return formatinfo_t::COMPONENTS_LENGTH;
        }

        static unsigned short getNBComponents()
        {
            return formatinfo_t::NB_COMPONENTS;
        }

        //Takes a regular non aligned or bitshifted value and sets it for the specified component!
        void SetComponentValue( e_COLOR_COMPONENTS cmp, data_t value )
        {
           m_colordata[FindComponent(cmp)] = value;
        }

        data_t GetComponentValue( e_COLOR_COMPONENTS cmp ) const
        {
            return m_colordata[FindComponent(cmp)];
        }

        data_t getRawValue()const
        {
            data_t         result = 0;
            unsigned short currentshift = 0; //the current length of the bitshift required

            for( int i = formatinfo_t::NB_COMPONENTS; i > 0 ; --i )
            {
                result       |= m_colordata[i] << currentshift;
                currentshift += formatinfo::COMPONENTS_LENGTH[i].second();
            }

            return result;
        }

        //data_t GetRawValue()const
        //{
        //    unsigned short redshift   = 0,
        //                   greenshift = 0,
        //                   blueshift  = 0;//used to shift the value later on !
        //    unsigned int redindex   = 0,
        //                 greenindex = 0,
        //                 blueindex  = 0;

        //    redindex   = FindComponent( e_CMP_RED,   redshift ); //Get the ammount of shift for component and get its position
        //    greenindex = FindComponent( e_CMP_GREEN, greenshift ); //Get the ammount of shift for component and get its position
        //    blueindex  = FindComponent( e_CMP_BLUE,  blueshift ); //Get the ammount of shift for component and get its position

        //    data_t redmask     = GetBitmaskComponent(redindex),
        //           greenmask   = GetBitmaskComponent(greenindex),
        //           bluemask    = GetBitmaskComponent(blueindex);

        //    //Shift and clean any leftover crap using the mask
        //    data_t cleanred   = (redmask   & ( m_colordata[redindex]   << redshift   ) ),
        //           cleangreen = (greenmask & ( m_colordata[greenindex] << greenshift ) ),
        //           cleanblue  = (bluemask  & ( m_colordata[blueindex]  << blueshift  ) );

        //    //Combine everything
        //    return ( cleanred | cleangreen | cleanblue );
        //}


        //void SetRawValue( data_t value )
        //{
        //    unsigned short redshift   = 0,
        //                   greenshift = 0,
        //                   blueshift  = 0;//used to shift the value later on !
        //    unsigned int redindex   = 0,
        //                 greenindex = 0,
        //                 blueindex  = 0;

        //    redindex   = FindComponent( e_CMP_RED,   &redshift ); //Get the ammount of shift for component and get its position
        //    greenindex = FindComponent( e_CMP_GREEN, &greenshift ); //Get the ammount of shift for component and get its position
        //    blueindex  = FindComponent( e_CMP_BLUE,  &blueshift ); //Get the ammount of shift for component and get its position

        //    //Those are simply masks that have the unique length of each components, 
        //    // yet are not aligned to the component's position in the actual raw value
        //    // like : 0110 0000 <- the 1 represents where the data is in the raw value.
        //    //        0000 0011 <- and this is the masks we're taking here. Notice that they're the same length!
        //    static const data_t LOW_BITS_RED_MASK = static_cast< data_t > ( CreateABitMask(formatinfo_t::COMPONENTS_LENGTH[redindex].second - 1) );
        //    static const data_t LOW_BITS_GREEN_MASK = static_cast< data_t > ( CreateABitMask(formatinfo_t::COMPONENTS_LENGTH[greenindex].second - 1) );
        //    static const data_t LOW_BITS_BLUE_MASK = static_cast< data_t > ( CreateABitMask(formatinfo_t::COMPONENTS_LENGTH[blueindex].second - 1) );
        //    
        //    //Shift and clean any leftover crap using the mask
        //    m_colordata[redindex]   = ( LOW_BITS_RED_MASK   & ( (value & LOW_BITS_RED_MASK) >> redshift ) ),
        //    m_colordata[greenindex] = ( LOW_BITS_GREEN_MASK & ( (value & (LOW_BITS_GREEN_MASK << greenshift) ) >> greenshift ) ),
        //    m_colordata[blueindex]  = ( LOW_BITS_BLUE_MASK  & ( (value & (LOW_BITS_RED_MASK << blueshift )) >> blueshift  ) );
        //}

        //Return the color in another format. changing to smaller formats will result in some data loss though 
        template<class DESTFORMAT>
            CColor<DESTFORMAT> ConvertTo()const
        {
            data_t redval   = m_colordata[FindComponent(e_CMP_RED)];
		    data_t greenval = m_colordata[FindComponent(e_CMP_GREEN)];
		    data_t blueval  = m_colordata[FindComponent(e_CMP_BLUE)];

            CColor<DESTFORMAT>::data_t destredval   = static_cast< CColor<DESTFORMAT>::data_t >(redval);
            CColor<DESTFORMAT>::data_t destgreenval = static_cast< CColor<DESTFORMAT>::data_t >(greenval);
            CColor<DESTFORMAT>::data_t destblueval  = static_cast< CColor<DESTFORMAT>::data_t >(blueval);

            return CColor<DESTFORMAT>( destredval, destgreenval, destblueval );
        }

	private:

        //Finds the component in the formatinfo component list, and return its index in said list.
        //Return the index of the component found. Second parameter is get the ammount of bitshift to do on the value to interpret it.
        int FindComponent( e_COLOR_COMPONENTS cmp, unsigned short * out_shiftdist = nullptr )const
        {
            unsigned short shifdist = 0;//used to shift the value later on !
            int i = formatinfo_t::NB_COMPONENTS-1;

            for( ; i > 0; --i )
            {
                if( formatinfo_t::COMPONENTS_LENGTH[i].first == cmp ) 
                {
                    if( out_shiftdist != nullptr )
                        *out_shiftdist = shifdist;
                    return i;
                }

                //Add to the bit shift distance
                shifdist += formatinfo_t::COMPONENTS_LENGTH[i].second;
            }

            throw std::exception("Fatal Error: Color component not found in current color format !");
            return -1;
        }

        ////Returns a bitmask for the component at the specified index
        //static const data_t GetBitmaskComponent( unsigned short componentindex )
        //{
        //    data_t mask = 0;
        //    unsigned short cmplen = formatinfo_t::COMPONENTS_LENGTH[componentindex].second;

        //    mask = static_cast< data_t > ( Mask<(cmplen-1)>::value );

        //    //Get the ammount to bitshift to
        //    unsigned short shiftsize = 0;
        //    for( unsigned short i = (componentindex-1); i > 0; --i )
        //        shiftsize += formatinfo_t::COMPONENTS_LENGTH[i].second;

        //    //bitshift to the right pos
        //    mask = mask << shiftsize;

        //    return mask;
        //}

        //Vars
        data_t m_colordata[formatinfo_t::NB_COMPONENTS];
        
	};

};

#endif